package com.diuse;

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import sun.misc.Unsafe;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

/**
 * -XX:PermSize=10M -XX:MaxPermSize=10M
 * 把MaxPermSize设置成2M，程序启动的是会抛出Permgen OOM，这个空间不够jvm加载所用的方法。动态链接出错就是这样原因。
 * Created by carey on 2016/7/9 0009.
 */
public class PermGen {

    /**
     * 这里并没有抛出PermGen OOM，从大gc的日志看，后面这里的空间就一直不变了，但是本机的其他内存倒是一直在涨，
     * 怀疑是不是intern这个native方法，后期是不是一直使用了直接内存？
     * <p/>
     * 将堆大小限制为20M，最后抛出的异常是
     * Exception in thread "main" java.lang.OutOfMemoryError: GC overhead limit exceeded
     * 这说明一个应用能存活的时间也是有上线的，gc次数的上线.
     */
    private static void allocateConstant() {
        List<String> list = new ArrayList<String>();
        long i = 0;
        while (true) {
            list.add(String.valueOf(i++).intern());
//            System.out.println(i);
        }
    }


    /**
     * 第一次使用intern()的时候应该是使用堆上的对象直接搬移到了永久区，然后直接返回这个对象。
     * <p/>
     * 但是不能理解的
     * 如果是直接搬移，那么为什么能够 == 返回true，我理解的== 是直接比较地址
     * 如果直接把堆划给了永久区？ 这样也不对，实现起来不方便，而且导致堆上空间不连续。
     */
    private static void testEqual() {
        String str1 = new StringBuilder("计算机").append("软件").toString();
        System.out.println(str1.intern() == str1);

        String str2 = new StringBuilder("ja").append("va1").toString();
        System.out.println(str2.intern() == str2);

    }


    //1073741823
    public static void main(String[] args) throws Exception {
//        allocateConstant();
//        testEqual();
        methodAreaOOM();
//        try{
//
//            directMemoryOOM();
//        }catch (Exception e ){
//            System.out.println("total allocate is : " + sum);
//            e.printStackTrace();
//        }
    }


    private static long sum = 0;

    public static void directMemoryOOM() throws Exception {
        Field unsafeField = Unsafe.class.getDeclaredFields()[0];
        unsafeField.setAccessible(true);
        Unsafe unsafe = (Unsafe) unsafeField.get(null);
        int i = 0;
        while (true) {
            long allocate = unsafe.allocateMemory(1024 * 1024);
            sum += allocate;
//            System.out.println(allocate);
        }

    }

    /**
     * 这里的本质其实和MaxPermSize设小差不多。
     * Enhancer这个需要好好学习下
     */
    private static void methodAreaOOM() {
        try {

            Enhancer enhancer = new Enhancer();
            enhancer.setSuperclass(OOMObject.class);
            enhancer.setUseCache(false);
            enhancer.setCallback(new MethodInterceptor() {

                //拦截器，在里面加入业务处理，比如日志，性能检测等
                public Object intercept(Object arg0, Method arg1, Object[] arg2, MethodProxy arg3) throws Throwable {
                    System.out.println("hello before");
                    //可以对拿到的结果做分析
                    Object o = arg3.invokeSuper(arg0, arg2);
                    System.out.println("result is: " + o);
                    System.out.println("hello after");
                    return o;
//                    return arg3.invokeSuper(arg0, arg2);
                }
            });
            OOMObject oob = (OOMObject) enhancer.create();
            oob.sayHello();
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
    }

    static class OOMObject {
        public void sayHello() {
            System.out.println("hello world");
            return;
        }


    }

}